#include "hi_i2c.h"
#include "ADS1X15.h"
#include <stdio.h>

/**************************************************************************/
/*!
    @brief  Sensor driver for the Adafruit ADS1X15 ADC breakouts.
*/
/**************************************************************************/
// class Adafruit_ADS1X15 {
// protected:
  // Instance-specific properties
  // Adafruit_I2CDevice *m_i2c_dev; ///< I2C bus device
  uint8_t m_bitShift;            ///< bit shift amount
  adsGain_t m_gain;              ///< ADC gain
  uint16_t m_dataRate;           ///< Data rate

  uint8_t ADS1X15_buffer[3];

  uint16_t MUX_BY_CHANNEL[] = {
    ADS1X15_REG_CONFIG_MUX_SINGLE_0, ///< Single-ended AIN0 //0x4000
    ADS1X15_REG_CONFIG_MUX_SINGLE_1, ///< Single-ended AIN1 //0x5000
    ADS1X15_REG_CONFIG_MUX_SINGLE_2, ///< Single-ended AIN2 //0x6000
    ADS1X15_REG_CONFIG_MUX_SINGLE_3  ///< Single-ended AIN3 //0x7000
};                                   ///< MUX config by channel

/**************************************************************************/
/*!
    @brief  Instantiates a new ADS1015 class w/appropriate properties
*/
/**************************************************************************/
ADS1015() {
  m_bitShift = 4;
  m_gain = GAIN_TWOTHIRDS; /* +/- 6.144V range (limited to VDD +0.3V max!) */
  m_dataRate = RATE_ADS1015_1600SPS;
}

/**************************************************************************/
/*!
    @brief  Instantiates a new ADS1115 class w/appropriate properties
*/
/**************************************************************************/
ADS1115() {
  m_bitShift = 0;
  m_gain = GAIN_TWOTHIRDS; /* +/- 6.144V range (limited to VDD +0.3V max!) */
  m_dataRate = RATE_ADS1115_128SPS;
}

/**************************************************************************/
/*!
    @brief  Sets up the HW (reads coefficients values, etc.)

    @param i2c_addr I2C address of device
    @param wire I2C bus

    @return true if successful, otherwise false
*/
/**************************************************************************/
bool ADS1X15_begin() {
  // uint8_t m_bitShift;            ///< bit shift amount
  // adsGain_t m_gain;              ///< ADC gain
  // uint16_t m_dataRate;           ///< Data rate
  
  m_bitShift = 0;
  m_gain = GAIN_TWOTHIRDS; /* +/- 6.144V range (limited to VDD +0.3V max!) */
  m_dataRate = RATE_ADS1115_128SPS;
  if (IoTI2cInit(0, 400*1000) != 0)
  {
    printf("IoTI2cInit failed\n");
  }
  return;
}

/**************************************************************************/
/*!
    @brief  Sets the gain and input voltage range

    @param gain gain setting to use
*/
/**************************************************************************/
void ADS1X15_setGain(adsGain_t gain) { m_gain = gain; }

/**************************************************************************/
/*!
    @brief  Gets a gain and input voltage range

    @return the gain setting
*/
/**************************************************************************/
adsGain_t getGain() { return m_gain; }

/**************************************************************************/
/*!
    @brief  Sets the data rate

    @param rate the data rate to use
*/
/**************************************************************************/
void setDataRate(uint16_t rate) { m_dataRate = rate; }

/**************************************************************************/
/*!
    @brief  Gets the current data rate

    @return the data rate
*/
/**************************************************************************/
uint16_t getDataRate() { return m_dataRate; }

/**************************************************************************/
/*!
    @brief  Gets a single-ended ADC reading from the specified channel

    @param channel ADC channel to read

    @return the ADC reading
*/
/**************************************************************************/
int16_t ADS1X15_readADC_SingleEnded(uint8_t channel) {

  if (channel > 3) {
    return 0;
  }

  startADCReading(MUX_BY_CHANNEL[channel], /*continuous=*/false);

  // Wait for the conversion to complete 等待转换完成
  while (!conversionComplete());

  // Read the conversion results
  return getLastConversionResults();
}

/**************************************************************************/
/*!
    @brief  Reads the conversion results, measuring the voltage
            difference between the P (AIN0) and N (AIN1) input.  Generates
            a signed value since the difference can be either
            positive or negative.

    @return the ADC reading
*/
/**************************************************************************/
int16_t readADC_Differential_0_1() {
  startADCReading(ADS1X15_REG_CONFIG_MUX_DIFF_0_1, /*continuous=*/false);

  // Wait for the conversion to complete
  while (!conversionComplete())
    ;

  // Read the conversion results
  return getLastConversionResults();
}

/**************************************************************************/
/*!
    @brief  Reads the conversion results, measuring the voltage
            difference between the P (AIN0) and N (AIN3) input.  Generates
            a signed value since the difference can be either
            positive or negative.
    @return the ADC reading
*/
/**************************************************************************/
int16_t readADC_Differential_0_3() {
  startADCReading(ADS1X15_REG_CONFIG_MUX_DIFF_0_3, /*continuous=*/false);

  // Wait for the conversion to complete
  while (!conversionComplete())
    ;

  // Read the conversion results
  return getLastConversionResults();
}

/**************************************************************************/
/*!
    @brief  Reads the conversion results, measuring the voltage
            difference between the P (AIN1) and N (AIN3) input.  Generates
            a signed value since the difference can be either
            positive or negative.
    @return the ADC reading
*/
/**************************************************************************/
int16_t readADC_Differential_1_3() {
  startADCReading(ADS1X15_REG_CONFIG_MUX_DIFF_1_3, /*continuous=*/false);

  // Wait for the conversion to complete
  while (!conversionComplete())
    ;

  // Read the conversion results
  return getLastConversionResults();
}

/**************************************************************************/
/*!
    @brief  Reads the conversion results, measuring the voltage
            difference between the P (AIN2) and N (AIN3) input.  Generates
            a signed value since the difference can be either
            positive or negative.

    @return the ADC reading
*/
/**************************************************************************/
int16_t readADC_Differential_2_3() {
  startADCReading(ADS1X15_REG_CONFIG_MUX_DIFF_2_3, /*continuous=*/false);

  // Wait for the conversion to complete
  while (!conversionComplete())
    ;

  // Read the conversion results
  return getLastConversionResults();
}

/**************************************************************************/
/*!
    @brief  Sets up the comparator to operate in basic mode, causing the
            ALERT/RDY pin to assert (go from high to low) when the ADC
            value exceeds the specified threshold.

            This will also set the ADC in continuous conversion mode.

    @param channel ADC channel to use
    @param threshold comparator threshold
*/
/**************************************************************************/
void startComparator_SingleEnded(uint8_t channel,
                                                   int16_t threshold) {
  // Start with default values
  uint16_t config =
      ADS1X15_REG_CONFIG_CQUE_1CONV |   // Comparator enabled and asserts on 1
                                        // match
      ADS1X15_REG_CONFIG_CLAT_LATCH |   // Latching mode
      ADS1X15_REG_CONFIG_CPOL_ACTVLOW | // Alert/Rdy active low   (default val)
      ADS1X15_REG_CONFIG_CMODE_TRAD |   // Traditional comparator (default val)
      ADS1X15_REG_CONFIG_MODE_CONTIN |  // Continuous conversion mode
      ADS1X15_REG_CONFIG_MODE_CONTIN;   // Continuous conversion mode

  // Set PGA/voltage range
  config |= m_gain;                     // 0

  // Set data rate
  config |= m_dataRate;

  config |= MUX_BY_CHANNEL[channel];

  // Set the high threshold register
  // Shift 12-bit results left 4 bits for the ADS1015
  writeRegister(ADS1X15_REG_POINTER_HITHRESH, threshold << m_bitShift);

  // Write config register to the ADC
  writeRegister(ADS1X15_REG_POINTER_CONFIG, config);
}

/**************************************************************************/
/*!
    @brief  In order to clear the comparator, we need to read the
            conversion results.  This function reads the last conversion
            results without changing the config value.

    @return the last ADC reading
*/
/**************************************************************************/
int16_t getLastConversionResults() {
  // Read the conversion results
  uint16_t res = readRegister(ADS1X15_REG_POINTER_CONVERT) >> m_bitShift;
  if (m_bitShift == 0) {
    return (int16_t)res;
  } else {
    // Shift 12-bit results right 4 bits for the ADS1015,
    // making sure we keep the sign bit intact
    if (res > 0x07FF) {
      // negative number - extend the sign to 16th bit
      res |= 0xF000;
    }
    return (int16_t)res;
  }
}

/**************************************************************************/
/*!
    @brief  Returns true if conversion is complete, false otherwise.

    @param counts the ADC reading in raw counts

    @return the ADC reading in volts
*/
/**************************************************************************/
float ADS1X15_computeVolts(int16_t counts) {
  // see data sheet Table 3
  float fsRange;
  switch (m_gain) {
  case GAIN_TWOTHIRDS:
    fsRange = 6.144f;
    break;
  case GAIN_ONE:
    fsRange = 4.096f;
    break;
  case GAIN_TWO:
    fsRange = 2.048f;
    break;
  case GAIN_FOUR:
    fsRange = 1.024f;
    break;
  case GAIN_EIGHT:
    fsRange = 0.512f;
    break;
  case GAIN_SIXTEEN:
    fsRange = 0.256f;
    break;
  default:
    fsRange = 0.0f;
  }
  // printf("%s[%s](%d): !!!%d,%f,%d!!! \n",__FILE__,__FUNCTION__,__LINE__,counts,fsRange,m_bitShift);

  // float a = 0.302;
  // float b = 128.101;
  // double c = 123;
  // float d = 112.64E3;
  // double e = 0.7623e-2;
  // float f = 1.23002398;
  // printf("a=%e \nb=%f \nc=%lf \nd=%lE \ne=%lf \nf=%f\n", a, b, c, d, e, f);

    // float ff = 251;
    // int w = 19.427;
    // int x = 92.78;
    // int y = 0.52;
    // int z = -87.27;   
    // printf("ff = %f, w = %d, x = %d, y = %d, z = %d\n", ff, w, x, y, z);

    // int a = 32768 >> m_bitShift;
    // printf("a:%d\n",a);
    // float b = fsRange/a;
    // printf("b:%f\n",b);
    // float c = counts * b;
    // printf("c:%f\n",c);

  return counts * (fsRange / (32768 >> m_bitShift));
}

/**************************************************************************/
/*!
    @brief  Non-blocking start conversion function

    Call getLastConversionResults() once conversionComplete() returns true.
    In continuous mode, getLastConversionResults() will always return the
    latest result.
    ALERT/RDY pin is set to RDY mode, and a 8us pulse is generated every
    time new data is ready.

    @param mux mux field value
    @param continuous continuous if set, otherwise single shot
*/
/**************************************************************************/
void startADCReading(uint16_t mux, bool continuous) {

  // Start with default values
  uint16_t config =
      ADS1X15_REG_CONFIG_CQUE_1CONV |   // Set CQUE to any value other than     //0x0000
                                        // None so we can use it in RDY mode
      ADS1X15_REG_CONFIG_CLAT_NONLAT |  // Non-latching (default val)           //0x0000
      ADS1X15_REG_CONFIG_CPOL_ACTVLOW | // Alert/Rdy active low   (default val) //0x0000
      ADS1X15_REG_CONFIG_CMODE_TRAD;    // Traditional comparator (default val) //0x0000

  if (continuous) {
    config |= ADS1X15_REG_CONFIG_MODE_CONTIN; //0x0000
  } else {
    config |= ADS1X15_REG_CONFIG_MODE_SINGLE; //0x1000
  }

  // Set PGA/voltage range
  config |= m_gain;       // 0x0000

  // Set data rate
  config |= m_dataRate;   // 0x0080

  // Set channels
  config |= mux;          //AIN3 0x7000

  // Set 'start single-conversion' bit  // 0x8000
  config |= ADS1X15_REG_CONFIG_OS_SINGLE; // 0xF180

  // Write config register to the ADC
  writeRegister(ADS1X15_REG_POINTER_CONFIG, config);      //0x01,0xF180

  // Set ALERT/RDY to RDY mode.
  writeRegister(ADS1X15_REG_POINTER_HITHRESH, 0x8000);    //0x30,0x8000
  writeRegister(ADS1X15_REG_POINTER_LOWTHRESH, 0x0000);   //0x20,0x0000
}

/**************************************************************************/
/*!
    @brief  Returns true if conversion is complete, false otherwise.

    @return True if conversion is complete, false otherwise.
*/
/**************************************************************************/
bool conversionComplete() {

  return (readRegister(ADS1X15_REG_POINTER_CONFIG) & 0x8000) != 0;
}

/**************************************************************************/
/*!
    @brief  Writes 16-bits to the specified destination register

    @param reg register address to write to
    @param value value to write to register
*/
/**************************************************************************/
void writeRegister(uint8_t reg, uint16_t value) {
  // printf("%s[%s](%d): !!!%X,%X!!! \n",__FILE__,__FUNCTION__,__LINE__,reg,value);
  ADS1X15_buffer[0] = reg;
  ADS1X15_buffer[1] = value >> 8;
  ADS1X15_buffer[2] = value & 0xFF;
  if (IoTI2cWrite(0,ADS1X15_ADDRESS, ADS1X15_buffer,3) != 0)
  {
    printf("IoTI2cWrite failed");
  }  
}

/**************************************************************************/
/*!
    @brief  Read 16-bits from the specified destination register

    @param reg register address to read from

    @return 16 bit register value read
*/
/**************************************************************************/
uint16_t readRegister(uint8_t reg) {

  ADS1X15_buffer[0] = reg;
  if (IoTI2cWrite(0,ADS1X15_ADDRESS, ADS1X15_buffer,1) != 0)
  {
    printf("IoTI2cWrite failed\n");
  }
  if (IoTI2cRead(0,ADS1X15_ADDRESS, ADS1X15_buffer,2)!= 0)
  {
    printf("IoTI2cRead failed\n");
  }
  return ((ADS1X15_buffer[0] << 8) | ADS1X15_buffer[1]);
}
